﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Qianke.Core.Common
{
    public class PriorityQueue<T>
    {
        Func<T, T, int> comparer;
        T[] heap;

        public int Count { get; private set; }


        public List<T> DataList
        {
            get
            {
                lock (heap)
                {
                    return heap.Take(Count).ToList();
                }
            }
        }

        public PriorityQueue(int capacity) : this(capacity, null) { }
        public PriorityQueue(Func<T, T, int> comparer) : this(16, comparer)
        {
        }


        public PriorityQueue(int capacity, Func<T, T, int> comparer)
        {
            this.comparer = comparer;
            this.heap = new T[capacity];
        }

        public void Push(T v)
        {
            lock (heap)
            {
                if (Count >= heap.Length) Array.Resize(ref heap, Count * 2);
                heap[Count] = v;
                SiftUp(Count++);
            }
        }

        public T Pop()
        {
            lock (heap)
            {
                var v = heap[0];
                heap[0] = heap[--Count];
                if (Count > 0) SiftDown(0);
                return v;
            }
        }

        public T Top()
        {
            lock (heap)
            {
                if (Count > 0) return heap[0];
                throw new InvalidOperationException("优先队列为空");
            }
        }

        void SiftUp(int n)
        {
            var v = heap[n];
            for (var n2 = n / 2; n > 0 && comparer(v, heap[n2]) > 0; n = n2, n2 /= 2) heap[n] = heap[n2];
            heap[n] = v;
        }

        void SiftDown(int n)
        {
            var v = heap[n];
            for (var n2 = n * 2; n2 < Count; n = n2, n2 *= 2)
            {
                if (n2 + 1 < Count && comparer(heap[n2 + 1], heap[n2]) > 0) n2++;
                if (comparer(v, heap[n2]) >= 0) break;
                heap[n] = heap[n2];
            }
            heap[n] = v;
        }
    }
}
